/*-
 * Copyright (C) 2018-2020, Shanghai Trusted Industry Control Platform Co., Ltd.
 * Created by 周正辉 (Zhenghui Zhou) <zhouzhenghui@ticpsh.com>, 赵楷文 (Kaiwen
 * Zhao) <zhaokaiwen@ticpsh.com>
 */

#ifndef TESTUDT_H
#define TESTUDT_H

#include <clang/AST/ASTConsumer.h>
#include <clang/AST/DeclBase.h>
#include <clang/AST/ExprCXX.h>
#include <clang/AST/QualTypeNames.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Analysis/AnalysisDeclContext.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/TextDiagnosticBuffer.h>
#include <clang/Parse/Parser.h>
#include <clang/Rewrite/Core/Rewriter.h>
#include <clang/StaticAnalyzer/Core/AnalyzerOptions.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/ADT/APFloat.h>
#include <llvm/Support/raw_ostream.h>

#include <boost/variant.hpp>
#include <iomanip>
#include <iostream>
#include <map>
#include <nlohmann/json.hpp>
#include <set>

#include "incparser/UDParser.h"
#include "testif.h"
#include "testvisitor.h"

namespace rocket {
namespace testif {

struct UDSpecInfo {
  nlohmann::json udspecs;
  std::vector<std::string> udspec_selected_ids;

  UDSpecInfo() {}
  UDSpecInfo(nlohmann::json user_def_specs,
             std::vector<std::string> user_selected_ids = {})
      : udspecs(std::move(user_def_specs)),
        udspec_selected_ids(std::move(user_selected_ids)) {}
};

/**
 * @brief 作为AST消费者插桩类
 * 作为观察者模式类继承自chang::ASTConsumer
 */
class TestUDTConsumer : public TestIfConsumer {
 private:
  std::unique_ptr<incparser::UDParser> udp;

  nlohmann::json &diags_j;
  /******
  nlohmann::json &types_j;
  nlohmann::json &literals_j;
  nlohmann::json &globals_j;
  nlohmann::json &functions_j;
  nlohmann::json &fixed_addresses_j;
  nlohmann::json &functiondecls_j;
  ********/

  nlohmann::json &udspecs_j;
  std::vector<std::string> &udspec_selected_ids;

 private:
  bool has_id = false;
  std::string spec_id = "";

 public:
  void setSpecID(const std::string &id) {
    spec_id = id;
    has_id = true;
  }
  void rmSpecID() {
    spec_id.clear();
    has_id = false;
  }

 protected:
  void elementCallBack(nlohmann::json &j) override {
    if (has_id) {
      j["@specid"].push_back(spec_id);
    }
  }

 public:
  TestUDTConsumer(const share::RocketAnalyzeSetting &setting,
                  nlohmann::json &diags_j, nlohmann::json &types_j,
                  nlohmann::json &literals_j, nlohmann::json &globals_j,
                  nlohmann::json &functions_j,
                  nlohmann::json &fixed_addresses_j,
                  /*nlohmann::json &functiondecls_j,*/ nlohmann::json &macros_j,
                  nlohmann::json &codelines_j, nlohmann::json &udspecs_j,
                  std::vector<std::string> &udspec_selected_ids)
      : TestIfConsumer(setting, types_j, literals_j, globals_j, functions_j,
                       fixed_addresses_j, /*functiondecls_j,*/ macros_j,
                       codelines_j),
        diags_j(diags_j),
        udspecs_j(udspecs_j),
        udspec_selected_ids(udspec_selected_ids) {}

  // 阻止因继承造成的重复解析
  virtual bool HandleTopLevelDecl(clang::DeclGroupRef) override { return true; }
  virtual void HandleTranslationUnit(clang::ASTContext &) override {}

  // 增量解析主要流程
  void handleEndSource() override;

  /**
   * @brief 挑选特化编号
   */
  void selectSpecId(nlohmann::json &j, const std::set<std::string> &id_set);

  std::vector<clang::TemplateArgument> parseTemplateArgsJson(
      const nlohmann::json &tas_j) const;

  std::map<clang::CXXRecordDecl *,
           std::vector<std::vector<clang::TemplateArgument>>>
  parseUDTypeSpecInfo(const nlohmann::json &info);

  std::pair<clang::FunctionDecl *, std::vector<clang::TemplateArgument>>
  parseUDFuncSpecInfo(const nlohmann::json &info);

  clang::QualType parseTemplateCXXRecord(std::string) const;

  void specFullCXXRecord(clang::ClassTemplateSpecializationDecl *cxxrd);

  //////////////////////////////

  std::string parse_type(const clang::QualType &type_) override;
};

}  // namespace testif
}  // namespace rocket

#endif  // TESTUDT_H
